/*  This file is part of -_-.

    -_- is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    -_- is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with -_-.  If not, see <http://www.gnu.org/licenses/>.

    Copyright 2012-2013 Oliver Katz */

#ifndef __COMMON_DEBUG_H
#define __COMMON_DEBUG_H

#include <stdio.h>
#include <string.h>
#include <execinfo.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>

#define Z_RED "\033[0;31m"
#define Z_YELLOW "\033[0;33m"
#define Z_BLUE "\033[0;34m"
#define Z_NORMAL "\033[0;0m"

#ifdef __cplusplus
    #include <iostream>
    #include <string>
    #include <sstream>
    #define Z_MSGCORE(cond, pref, msg) {if(cond){cout << pref << msg << "\n";}}
    #define Z_DEBUG(cond, msg) Z_MSGCORE(cond, "- ", z_basename((char *)__FILE__) << ":" << Z_BLUE << __LINE__ << Z_NORMAL << " in " << __FUNCTION__ << " - " << msg)
    #define Z_DEBUG_IN(cond, in) Z_MSGCORE(cond, "- ", z_basename((char *)__FILE__) << ":" << __LINE__ << " in " << __FUNCTION__ << " <= " << in)
    #define Z_DEBUG_OUT(cond, out) Z_MSGCORE(cond, "- ", z_basename((char *)__FILE__) << ":" << __LINE__ << " in " << __FUNCTION__ << " => " << out)
    #define Z_WARNING(msg) {Z_MSGCORE(true, "- ", z_basename((char *)__FILE__) << ":" << Z_BLUE << __LINE__ << Z_NORMAL << " in " << __FUNCTION__ << " - " << Z_YELLOW << msg << Z_NORMAL);z_dump_stack();}
    #define Z_WARNING_NULL(ptr) {if(ptr)Z_WARNING("cannot operate on null object: " << #ptr);}
    #define Z_NOTE(name, val) {cout << "  - " << name << " - " << val << "\n";}
#else
    #define Z_MSGCORE(cond, pref, msg, ...) {if(cond){printf(pref); printf(msg, ##__VA_ARGS__);}}
    #define Z_DEBUG(cond, msg, ...) Z_MSGCORE(cond, "- ", "%s:%s%i%s in %s - " msg, ##__VA_ARGS__, z_basename(__FILE__), Z_BLUE, __LINE__, Z_NORMAL, __FUNCTION__)
    #define Z_DEBUG_IN(cond, msg, ...) Z_MSGCORE(cond, "- ", "%s:%s%i%s in %s <= " msg, z_basename(__FILE__), Z_BLUE, __LINE__, Z_NORMAL, __FUNCTION__, ##__VA_ARGS__)
    #define Z_DEBUG_OUT(cond, msg, ...) Z_MSGCORE(cond, "- ", "%s:%s%i%s in %s => " msg, z_basename(__FILE__), Z_BLUE, __LINE__, Z_NORMAL, __FUNCTION__, ##__VA_ARGS__)
    #define Z_WARNING(msg, ...) {Z_MSGCORE(1, "- ", "%s:%s%i%s in %s - " Z_YELLOW msg Z_NORMAL "\n", z_basename(__FILE__), Z_BLUE, __LINE__, Z_NORMAL, __FUNCTION__, ##__VA_ARGS__);z_dump_stack();}
    #define Z_WARNING_NULL(ptr, rv) if(ptr==NULL){Z_WARNING("cannot operate on null object: %s", #ptr);return rv;}
    #define Z_NOTE(name, val, ...) {printf("  - %s - " val "\n", name, ##__VA_ARGS__);}
#endif

#define Z_DEBUG_INIT(argv0) _z_debug_binpath=argv0
extern char *_z_debug_binpath;
extern int _z_debug_color;

#ifdef __cplusplus
extern "C"
{
#endif
    void z_dump_stack();
    char *z_basename(char *p);
#ifdef __cplusplus
}
#endif

#endif

/// Debugging with Z_DEBUG is pretty simple. Here's a couple of examples:
/// {{bool verbose = false;
/// Z_DEBUG(verbose, "hi"); // won't display
/// verbose = true;
/// Z_DEBUG(verbose, "hi"); // will display
/// Z_DEBUG(verbose, "hi" << 5); // will display "hi5" in C++
/// Z_DEBUG(verbose, "hi%i", i); // will display "hi5" in C}}
///
/// To show input and output flow within functions, use the following:
/// {{Z_DEBUG_IN(verbose, "this is coming into the function as an input");
/// Z_DEBUG_OUT(verbose, "this is being returned by the function as an output");}}
///
/// You also have Z_WARNING:
/// {{Z_WARNING("this is a warning");}}
///
/// At the beginning of a function, if you want to check a pointer and give a warning if it's null (also returns returnValue),
/// {{Z_WARNING_NULL(ptr, returnValue);}}